import os
import shutil
import socket
import threading
import subprocess
import sys
from http.server import HTTPServer, SimpleHTTPRequestHandler
import webbrowser
import tkinter as tk
from tkinter import ttk, messagebox, filedialog

# ========== 路径设置（兼容 .exe 打包）==========
def get_base_dir():
    if getattr(sys, 'frozen', False):
        return os.path.dirname(sys.executable)
    else:
        return os.path.dirname(__file__)

BASE_DIR = get_base_dir()
OUTPUT_DIR = os.path.join(BASE_DIR, "output")
POSTER_DIR = os.path.join(OUTPUT_DIR, "posters")
TXT_FILE = os.path.join(OUTPUT_DIR, "movies.txt")
M3U_FILE = os.path.join(OUTPUT_DIR, "movies.m3u")
CONFIG_FILE = os.path.join(BASE_DIR, "config.txt")

os.makedirs(POSTER_DIR, exist_ok=True)

# ========== 获取本机 IP ==========
def get_local_ip():
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.connect(("8.8.8.8", 80))
        ip = s.getsockname()[0]
        s.close()
        return ip
    except Exception:
        return "127.0.0.1"

LOCAL_IP = get_local_ip()

# 全局变量
entries = []
server_thread = None
httpd = None
is_server_running = False
SERVER_PORT = 9013  # 🔥 已修改为 9013
SERVER_URL = f"http://{LOCAL_IP}:{SERVER_PORT}"

# ========== 读写配置文件 ==========
def load_config():
    if not os.path.exists(CONFIG_FILE):
        return False
    try:
        with open(CONFIG_FILE, 'r', encoding='utf-8') as f:
            return f.read().strip() == 'True'
    except:
        return False

def save_config(auto_start):
    try:
        with open(CONFIG_FILE, 'w', encoding='utf-8') as f:
            f.write(str(auto_start))
    except Exception as e:
        messagebox.showerror("保存失败", f"无法保存配置：{e}")

# ========== 文件操作 ==========
def load_data():
    global entries
    entries.clear()
    if not os.path.exists(M3U_FILE):
        with open(M3U_FILE, 'w', encoding='utf-8') as f:
            f.write("#EXTM3U\n")
        return
    try:
        with open(M3U_FILE, 'r', encoding='utf-8') as f:
            lines = [line.strip() for line in f.readlines()]
    except Exception:
        return
    i = 0
    while i < len(lines):
        line = lines[i]
        if line.startswith("#EXTINF") and "," in line:
            name = line.rsplit(",", 1)[1]
            logo_ext = ".jpg"
            if 'tvg-logo="' in line:
                part = line.split('tvg-logo="')[1].split('"')[0]
                ext = os.path.splitext(part)[1]
                if ext.lower() in ['.jpg','.jpeg','.png','.gif','.bmp','.webp']:
                    logo_ext = ext
            i += 1
            if i < len(lines) and not lines[i].startswith("#"):
                url = lines[i]
                entries.append({"name": name, "url": url, "logo_ext": logo_ext})
        i += 1
    rewrite_txt_from_entries()

def rewrite_txt_from_entries():
    with open(TXT_FILE, 'w', encoding='utf-8') as f:
        for e in entries:
            f.write(f"{e['name']},{e['url']}\n")

def save_all_files(use_http=False):
    base_url = f"{SERVER_URL}/posters" if use_http else "posters"
    try:
        with open(M3U_FILE, 'w', encoding='utf-8') as f:
            f.write("#EXTM3U x-tvg-url=\"\"\n")
            for e in entries:
                logo_path = f"{base_url}/{e['name']}{e['logo_ext']}".replace("\\", "/")
                f.write(f'#EXTINF:-1 tvg-logo="{logo_path}",{e["name"]}\n')
                f.write(f"{e["url"]}\n")
        rewrite_txt_from_entries()
    except Exception as e:
        messagebox.showerror("保存失败", str(e))

# ========== 添加防火墙规则 ==========
def add_firewall_rule(port):
    if os.name != 'nt':
        return True
    try:
        rule_name = f"MovieServer_Port_{port}"
        cmd = [
            "netsh", "advfirewall", "firewall", "add", "rule",
            f"name={rule_name}",
            "dir=in", "action=allow", "protocol=TCP",
            f"localport={port}"
        ]
        subprocess.check_output(cmd, stderr=subprocess.STDOUT)
        return True
    except subprocess.CalledProcessError as e:
        msg = f"防火墙规则添加失败，可能需要以管理员身份运行程序。\n" \
              f"命令：{' '.join(e.cmd)}\n" \
              f"错误：{e.output.decode('gbk', errors='ignore')}"
        messagebox.showwarning("防火墙警告", msg)
        return False
    except Exception as e:
        messagebox.showerror("错误", f"无法添加防火墙规则：{str(e)}")
        return False

# ========== 本地HTTP服务器 ==========
class NoLogHandler(SimpleHTTPRequestHandler):
    def log_message(self, format, *args): pass

def start_server():
    global httpd, is_server_running
    try:
        os.chdir(OUTPUT_DIR)
        httpd = HTTPServer(("", SERVER_PORT), NoLogHandler)
        is_server_running = True
        update_server_status()
        httpd.serve_forever()
    except OSError as e:
        if "address already in use" in str(e).lower():
            messagebox.showerror("错误", f"端口 {SERVER_PORT} 已被占用，请换一个。")
        else:
            messagebox.showerror("错误", str(e))
    except Exception as e:
        messagebox.showerror("错误", str(e))
    finally:
        if is_server_running:
            is_server_running = False
            update_server_status()

def run_server():
    global server_thread
    if not is_server_running:
        server_thread = threading.Thread(target=start_server, daemon=True)
        server_thread.start()

def stop_server():
    global httpd
    if httpd:
        httpd.shutdown()
        httpd.server_close()
    update_server_status()

def toggle_server():
    global SERVER_PORT, SERVER_URL
    try:
        port = int(port_entry.get().strip())
        if not (1024 <= port <= 65535):
            raise ValueError("端口必须在 1024-65535 之间")
    except ValueError as ve:
        messagebox.showwarning("警告", str(ve))
        return
    SERVER_PORT = port
    SERVER_URL = f"http://{LOCAL_IP}:{SERVER_PORT}"
    if is_server_running:
        stop_server()
    else:
        success = add_firewall_rule(SERVER_PORT)
        if not success:
            if not messagebox.askyesno("继续？", "防火墙设置失败，是否仍要启动服务器？"):
                return
        run_server()

def update_server_status():
    if is_server_running:
        status_label.config(text=f"🟢 服务器运行中: {SERVER_URL}", foreground="green")
        btn_start_server.config(text="⏹️ 停止服务")
    else:
        status_label.config(text="🔴 服务器未运行", foreground="red")
        btn_start_server.config(text="▶️ 启动服务")

# ========== 剪贴板复制 ==========
def copy_to_clipboard(text):
    try:
        root.clipboard_clear()
        root.clipboard_append(text)
        root.update()
        messagebox.showinfo("已复制", f"已复制到剪贴板：\n{text}")
    except Exception as e:
        messagebox.showerror("失败", str(e))

# ========== GUI 操作函数 ==========
def add_entry():
    name = entry_movie.get().strip()
    url = entry_play.get().strip()
    poster = entry_poster.get().strip()
    if not name or not url:
        messagebox.showwarning("警告", "请填写电影名称和播放地址！")
        return
    for i, e in enumerate(entries):
        if e["name"] == name:
            if messagebox.askyesno("确认", f"已存在 '{name}'，是否替换？"):
                del entries[i]; break
            else: return
    ext = ".jpg"
    if poster:
        _, e = os.path.splitext(poster.lower())
        ext = e if e in ['.jpg','.jpeg','.png','.gif','.bmp','.webp'] else ".jpg"
        dest = os.path.join(POSTER_DIR, f"{name}{ext}")
        try: 
            shutil.copy(poster, dest)
        except Exception as e: 
            messagebox.showerror("错误", f"封面保存失败：{e}"); return
    entries.insert(0, {"name": name, "url": url, "logo_ext": ext})
    save_all_files(use_http=is_server_running)
    refresh_listbox(); clear_inputs()
    messagebox.showinfo("成功", f"'{name}' 添加成功！")

def select_poster():
    path = filedialog.askopenfilename(
        title="选择封面图片",
        filetypes=[("图片文件", "*.jpg *.jpeg *.png *.bmp *.gif *.webp")]
    )
    if path:
        entry_poster.delete(0, tk.END)
        entry_poster.insert(0, path)

def clear_inputs():
    entry_movie.delete(0, tk.END)
    entry_play.delete(0, tk.END)
    entry_poster.delete(0, tk.END)

def refresh_listbox():
    listbox.delete(0, tk.END)
    for e in entries:
        listbox.insert(tk.END, f"🎬 {e['name']} | 📺 {e['url']}")

def delete_selected():
    sel = listbox.curselection()
    if not sel:
        messagebox.showwarning("警告", "请先选择要删除的电影！")
        return
    idx = sel[0]
    name = entries[idx]["name"]
    del entries[idx]
    save_all_files(use_http=is_server_running)
    refresh_listbox()
    messagebox.showinfo("删除成功", f"'{name}' 已删除。")

def search_douban():
    name = entry_movie.get().strip()
    if not name:
        messagebox.showwarning("警告", "请输入电影名称！")
        return
    webbrowser.open(f"https://search.douban.com/movie/subject_search?search_text={name}")

def test_stream():
    url = entry_play.get().strip()
    if not url:
        sel = listbox.curselection()
        if not sel:
            messagebox.showwarning("警告", "请先输入或选择播放地址！")
            return
        url = entries[sel[0]]["url"]
    if not url.startswith("http"): 
        url = "https://" + url
    webbrowser.open(f" https://players.akamai.com/players/hlsjs?streamUrl={url}")

def generate_public_m3u():
    save_all_files(use_http=True)
    messagebox.showinfo("发布成功", f"已生成带网络路径的文件：\n{SERVER_URL}/movies.m3u")

def show_autostart_help():
    help_text = (
        "📌 设置开机自启方法（Windows）：\n"
        "1. 按 Win+R，输入 shell:startup 回车\n"
        "2. 把这个程序的快捷方式拖进去\n"
        "3. 下次开机就会自动运行"
    )
    messagebox.showinfo("开机启动说明", help_text)

def on_auto_start_change():
    save_config(auto_start_var.get())

# ================================
# 🔽 新增：系统托盘功能
# ================================
try:
    from PIL import Image, ImageDraw
    import pystray
except ImportError:
    messagebox.showerror("缺少依赖", "请安装 pystray 和 pillow：\npip install pystray pillow")
    sys.exit(1)

tray_icon = None
root_visible = True

def create_image():
    """生成一个简单的图标"""
    width = 64
    height = 64
    image = Image.new('RGB', (width, height), (255, 100, 100))
    dc = ImageDraw.Draw(image)
    dc.rectangle((width//4, height//4, width*3//4, height*3//4), fill=(0, 0, 0))
    return image

def on_tray_open(icon, item):
    root.deiconify()
    root.lift()
    root.focus_force()
    icon.visible = False

def on_tray_toggle_server(icon, item):
    toggle_server()

def on_tray_exit(icon, item):
    stop_server()
    icon.stop()
    root.quit()
    root.destroy()

def setup_tray_icon():
    """创建并运行系统托盘图标"""
    global tray_icon
    icon_path = os.path.join(BASE_DIR, "1.ico")
    if os.path.exists(icon_path):
        try:
            image = Image.open(icon_path)
        except:
            image = create_image()
    else:
        image = create_image()

    menu = pystray.Menu(
        pystray.MenuItem("🎬 打开主界面", on_tray_open),
        pystray.MenuItem("⏯️ 启动/停止服务", on_tray_toggle_server),
        pystray.MenuItem("❌ 退出程序", on_tray_exit)
    )

    tray_icon = pystray.Icon("movie_manager", image, "电影源管理器", menu)
    tray_icon.run()

def minimize_to_tray():
    """最小化到托盘"""
    global root_visible
    root.withdraw()
    root_visible = False

def close_window():
    """拦截关闭事件"""
    minimize_to_tray()

# ========== 主界面 & 初始化 ==========
root = tk.Tk()
root.title("🎬 电影源管理器 - 支持防火墙自动放行")
root.geometry("800x780")
root.resizable(False, False)

# 拦截关闭按钮
root.protocol("WM_DELETE_WINDOW", close_window)

# --- 服务器设置 ---
port_frame = tk.LabelFrame(root, text="🔧 本地服务器", padx=10, pady=10)
port_frame.pack(padx=15, pady=10, fill="x")
tk.Label(port_frame, text="端口号：").grid(row=0, column=0, sticky="w", pady=2)
port_entry = ttk.Entry(port_frame, width=10)
port_entry.insert(0, "9013")  # 🔥 默认值改为 9013
port_entry.grid(row=0, column=1, padx=5, pady=2)
btn_start_server = ttk.Button(port_frame, text="▶️ 启动服务", width=15, command=toggle_server)
btn_start_server.grid(row=0, column=2, padx=10)
status_label = tk.Label(port_frame, text="🔴 服务器未运行", font=("Arial", 10), fg="red")
status_label.grid(row=1, column=0, columnspan=3, sticky="w", pady=5)
btn_row = tk.Frame(port_frame)
ttk.Button(btn_row, text="📂 打开 output", command=lambda: os.startfile(OUTPUT_DIR)).pack(side=tk.LEFT, padx=2)
ttk.Button(btn_row, text="📋 复制 m3u 地址", command=lambda: copy_to_clipboard(f"{SERVER_URL}/movies.m3u")).pack(side=tk.LEFT, padx=2)
ttk.Button(btn_row, text="📋 复制 txt 地址", command=lambda: copy_to_clipboard(f"{SERVER_URL}/movies.txt")).pack(side=tk.LEFT, padx=2)
ttk.Button(btn_row, text="📤 生成公网文件", command=generate_public_m3u).pack(side=tk.LEFT, padx=2)
btn_row.grid(row=2, column=0, columnspan=3, pady=5)
tk.Label(port_frame, text="提示：点击【启动服务】将自动放行防火墙", fg="gray").grid(row=3, column=0, columnspan=3, sticky="w")

# --- 输入区域 ---
input_frame = tk.LabelFrame(root, text="➕ 添加新电影", padx=10, pady=10)
input_frame.pack(padx=15, pady=10, fill="x")
row1 = tk.Frame(input_frame)
tk.Label(row1, text="电影名称：").pack(side=tk.LEFT)
entry_movie = ttk.Entry(row1, width=40)
entry_movie.pack(side=tk.LEFT, padx=5)
ttk.Button(row1, text="豆瓣搜索", width=12, command=search_douban).pack(side=tk.LEFT)
row1.pack(pady=5)
row2 = tk.Frame(input_frame)
tk.Label(row2, text="播放地址：").pack(side=tk.LEFT)
entry_play = ttk.Entry(row2, width=40)
entry_play.pack(side=tk.LEFT, padx=5)
ttk.Button(row2, text="视频测试", width=12, command=test_stream).pack(side=tk.LEFT)
row2.pack(pady=5)
row3 = tk.Frame(input_frame)
tk.Label(row3, text="封面图片：").pack(side=tk.LEFT)
entry_poster = ttk.Entry(row3, width=40)
entry_poster.pack(side=tk.LEFT, padx=5)
ttk.Button(row3, text="浏览...", command=select_poster).pack(side=tk.LEFT)
row3.pack(pady=5)
ttk.Button(input_frame, text="✅ 添加到列表", width=30, padding=10, command=add_entry).pack(pady=10)

# --- 列表显示 ---
list_frame = tk.LabelFrame(root, text="📋 已添加的电影", padx=10, pady=10)
list_frame.pack(padx=15, pady=10, fill="both", expand=True)
listbox = tk.Listbox(list_frame, height=10, font=("Courier", 10))
listbox.pack(fill="both", expand=True)
ttk.Button(root, text="🗑️ 删除选中项", width=22, command=delete_selected).pack(pady=5)

# --- 开机与自动启动 ---
auto_frame = tk.LabelFrame(root, text="⚙️ 启动设置", padx=10, pady=10)
auto_frame.pack(padx=15, pady=10, fill="x")
auto_start_var = tk.BooleanVar(value=load_config())
chk_auto = tk.Checkbutton(auto_frame, text="☑ 开机后自动启动服务器", variable=auto_start_var, command=on_auto_start_change)
chk_auto.pack(anchor="w")
ttk.Button(auto_frame, text="📘 如何设置开机自启", command=show_autostart_help).pack(anchor="w", pady=5)

# ========== 初始化 ==========
load_data()
refresh_listbox()
update_server_status()

if auto_start_var.get():
    root.after(100, toggle_server)

root.bind('<Return>', lambda e: add_entry())

# ---------- 启动系统托盘 ----------
tray_thread = threading.Thread(target=setup_tray_icon, daemon=True)
tray_thread.start()

# 默认最小化到托盘
minimize_to_tray()

root.mainloop()